package com.heima.article.service.Impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.article.mapper.ApArticleConfigMapper;
import com.heima.article.mapper.ApArticleContentMapper;
import com.heima.article.mapper.ApArticleMapper;
import com.heima.article.service.ApArticleFreemarkerService;
import com.heima.article.service.ApArticleService;
import com.heima.common.redis.CacheService;
import com.heima.model.article.dtos.ArticleDto;
import com.heima.model.article.dtos.ArticleHomeDto;
import com.heima.model.article.dtos.ArticleInfoDto;
import com.heima.model.article.pojos.ApArticle;
import com.heima.model.article.pojos.ApArticleConfig;
import com.heima.model.article.pojos.ApArticleContent;
import com.heima.model.article.pojos.HotArticle;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.mess.ArticleVisitStreamMess;
import com.heima.model.user.pojos.ApUser;
import com.heima.utils.thread.AppThreadLocalUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.net.nntp.Article;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

import static com.heima.common.constants.ArticleConstants.*;
import static com.heima.common.constants.BehaviorConstants.*;
import static com.heima.common.constants.UserConstants.USER_FOLLOWING_LIST;


@Service
@Transactional
@Slf4j
public class ApArticleServiceImpl extends ServiceImpl<ApArticleMapper, ApArticle> implements ApArticleService {

    @Resource
    private ApArticleConfigMapper apArticleConfigMapper;
    @Resource
    private ApArticleContentMapper apArticleContentMapper;
    @Resource
    private ApArticleMapper apArticleMapper;
    @Resource
    private ApArticleFreemarkerService apArticleFreemarkerService;

    @Resource
    private CacheService cacheService;

    private static final Integer MAX_PAGE_SIZE = 50;


    /**
     * 加载文章列表
     *
     * @param dto  文章列表查询参数
     * @param type 文章类型
     * @return 文章列表
     */
    @Override
    public ResponseResult load(ArticleHomeDto dto, Short type) {
        if (dto.getMinBehotTime() == null) dto.setMinBehotTime(new Date());
        if (dto.getMaxBehotTime() == null) dto.setMaxBehotTime(new Date());
        Integer size = dto.getSize();
        if (size == null || size < 10) {
            size = 10;
        }
        size = Math.min(size, MAX_PAGE_SIZE);
        dto.setSize(size);
        if (!type.equals(LOADTYPE_LOAD_MORE) && !type.equals(LOADTYPE_LOAD_NEW)) {
            type = LOADTYPE_LOAD_NEW;
        }
        if (dto.getTag() == null) {
            dto.setTag(DEFAULT_TAG);
        }

        List<Article> articles = apArticleMapper.loadArticleList(dto, type);
        return ResponseResult.okResult(articles);
    }

    @Override
    public ResponseResult load2(ArticleHomeDto dto, Short type, Boolean isFirstPage) {

        if (isFirstPage) {
            String json = cacheService.get(HOT_ARTICLE_FIRST_PAGE + dto.getTag());
            List<HotArticle> hotArticles = JSON.parseArray(json, HotArticle.class);
            return ResponseResult.okResult(hotArticles);
        }
        return load(dto, type);
    }

    /**
     * 保存文章
     *
     * @param dto 文章信息
     * @return 保存结果
     */
    @Override
    public ResponseResult saveArticle(ArticleDto dto) {
        // 记录日志：开始进入保存文章的方法
        log.info("开始执行保存文章的方法。");

        if (dto == null) {
            // 记录日志：错误，文章数据为空
            log.error("错误：文章数据为空。");
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }

        ApArticle apArticle = new ApArticle();
        BeanUtils.copyProperties(dto, apArticle);

        if (apArticle.getId() == null) {
            // 保存文章信息
            save(apArticle);
            // 记录日志：文章信息已保存
            log.info("文章信息已保存。");

            // 保存文章配置信息
            ApArticleConfig apArticleConfig = new ApArticleConfig(apArticle.getId());
            apArticleConfigMapper.insert(apArticleConfig);
            // 记录日志：文章配置信息已保存
            log.info("文章配置信息已保存。");

            // 保存文章内容信息
            ApArticleContent apArticleContent = new ApArticleContent();
            apArticleContent.setArticleId(apArticle.getId());
            apArticleContent.setContent(dto.getContent());
            apArticleContentMapper.insert(apArticleContent);
            // 记录日志：文章内容信息已保存
            log.info("文章内容信息已保存。");
        } else {
            // 更新文章信息
            updateById(apArticle);
            // 记录日志：文章信息已更新
            log.info("文章信息已更新。");

            // 更新文章内容信息
            ApArticleContent apArticleContent = apArticleContentMapper.selectOne(Wrappers.<ApArticleContent>lambdaQuery().eq(ApArticleContent::getArticleId, apArticle.getId()));
            apArticleContent.setContent(dto.getContent());
            apArticleContentMapper.updateById(apArticleContent);
            // 记录日志：文章内容信息已更新
            log.info("文章内容信息已更新。");
        }

        try {
            apArticleFreemarkerService.buildArticleToMinIo(apArticle, dto.getContent());
            // 记录日志：文章成功构建到MinIO
            log.info("文章成功构建到MinIO。");
        } catch (Exception e) {
            log.error("构建文章到MinIO失败", e);
        }

        // 记录日志：保存文章的方法执行完毕，输出结果
        log.info("保存文章的方法执行完毕。输出结果为:{}", apArticle.getId());

        return ResponseResult.okResult(apArticle.getId());
    }

    @Override
    public ResponseResult loadArticleBehavior(ArticleInfoDto dto) {
        if (dto.getArticleId() == null || dto.getAuthorId() == null) {

            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }

        ApUser user = AppThreadLocalUtil.getUser();
        if (user == null) {

            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        HashMap<String, Object> map = new HashMap<>();

        boolean isFollow = Follow(user);

        boolean isLike = Like(dto, user);
        boolean isUnLike = UnLike(dto, user);
        boolean isCollection = Collection(dto, user);
        map.put("isfollow", isFollow);
        map.put("isunlike", isUnLike);
        map.put("iscollection", isCollection);
        map.put("islike", isLike);

        return ResponseResult.okResult(map);
    }

    @Override
    public void updateScore(ArticleVisitStreamMess articleVisitStreamMess) {
        Long articleId = articleVisitStreamMess.getArticleId();
        ApArticle apArticle = getById(articleId);
        if (apArticle == null) {
            return;
        }
        Integer ArticleScore = computeArticleScore(apArticle, articleVisitStreamMess);
        ArticleScore = ArticleScore * 3;
        //更新频道分数
        updateChannelScore(apArticle, ArticleScore, HOT_ARTICLE_FIRST_PAGE + apArticle.getChannelId());

        //更新推荐分数
        updateChannelScore(apArticle, ArticleScore, HOT_ARTICLE_FIRST_PAGE + DEFAULT_TAG);

    }

    private void updateChannelScore(ApArticle apArticle, Integer ArticleScore, String Channel) {
        String articlesJsonStr = cacheService.get(Channel);
        if (StringUtils.isBlank(articlesJsonStr)) {
            return;
        }
        List<HotArticle> hotArticles = JSON.parseArray(articlesJsonStr, HotArticle.class);
        hotArticles = hotArticles.stream().sorted(Comparator.comparing(HotArticle::getScore).reversed()).collect(Collectors.toList());

        if (CollectionUtils.isEmpty(hotArticles)) {
            return;
        }
        boolean isMatchingArticleId = false;
        for (HotArticle hotArticle : hotArticles) {
            if (hotArticle.getId().equals(apArticle.getId())) {
                hotArticle.setScore(ArticleScore);
                isMatchingArticleId = true;
                break;
            }
        }

        //缓存中没有对应的文章时
        if (BooleanUtils.isFalse(isMatchingArticleId)) {

            if (hotArticles.size() >= 30) {


                HotArticle hotArticle = hotArticles.get(hotArticles.size() - 1);
                if (hotArticle.getScore() < ArticleScore) {
                    hotArticles.remove(hotArticle);

                    BeanUtils.copyProperties(apArticle, hotArticle);
                    hotArticle.setScore(ArticleScore);

                    hotArticles.add(hotArticle);
                }


            } else {
                HotArticle hotArticle = new HotArticle();
                BeanUtils.copyProperties(apArticle, hotArticle);
                hotArticle.setScore(ArticleScore);
                hotArticles.add(hotArticle);
            }

        }
        hotArticles = hotArticles.stream().sorted(Comparator.comparing(HotArticle::getScore).reversed()).collect(Collectors.toList());

        cacheService.set(Channel, JSON.toJSONString(hotArticles));
    }

    private Integer computeArticleScore(ApArticle apArticle, ArticleVisitStreamMess articleVisitStreamMess) {
        apArticle.setCollection(apArticle.getCollection() == null ? 0 : apArticle.getCollection() + articleVisitStreamMess.getCollect());
        apArticle.setComment(apArticle.getComment() == null ? 0 : apArticle.getComment() + articleVisitStreamMess.getComment());
        apArticle.setLikes(apArticle.getLikes() == null ? 0 : apArticle.getLikes() + articleVisitStreamMess.getLike());
        apArticle.setViews(apArticle.getViews() == null ? 0 : apArticle.getLikes() + articleVisitStreamMess.getView());
        updateById(apArticle);
        int colScore = apArticle.getCollection() + HOT_ARTICLE_COLLECTION_WEIGHT;
        int comScore = apArticle.getComment() + HOT_ARTICLE_COMMENT_WEIGHT;
        int likScore = apArticle.getLikes() + HOT_ARTICLE_LIKE_WEIGHT;
        int vieScore = apArticle.getViews() + HOT_ARTICLE_VIEWS_WEIGHT;

        return colScore + comScore + likScore + vieScore;
    }

    private boolean Collection(ArticleInfoDto dto, ApUser user) {
        return null != cacheService.hGet(COLLECTION_BEHAVIOR + user.getId(), dto.getArticleId().toString());
    }

    private boolean UnLike(ArticleInfoDto dto, ApUser user) {
        return null != cacheService.hGet(UNLIKE_BEHAVIOR + dto.getArticleId(), user.getId().toString());
    }

    private boolean Like(ArticleInfoDto dto, ApUser user) {

        return null != cacheService.hGet(LIKE_BEHAVIOR + dto.getArticleId(), user.getId().toString());
    }

    private boolean Follow(ApUser user) {
        Set<String> follow = cacheService.zRange(USER_FOLLOWING_LIST + user.getId(), 0, -1);
        return follow.size() > 0;
    }
}
